#include "stdafx.h"
#include "windows.h"
#include "GPSMetadataDB.h"
#include "gps.pb.h"
#include <iostream>

int gps_metakey_compare(Db *dbp, const Dbt *key1, const Dbt *key2)
{
	time_t t1, t2;
	memcpy(&t1, key1->get_data(), sizeof(time_t));
	memcpy(&t2, key2->get_data(), sizeof(time_t));
	return (t1 - t2);
}

#define gps_recvtimeindex_compare gps_metakey_compare

int gps_index_recvtime_extractor(Db * sdbp, const Dbt *pkey, const Dbt *pdata, Dbt *skey)
{
	static time_t keyval;
	gps::gpsmeta obj;
	std::string strData((char*)pdata->get_data());
	obj.ParseFromString(strData);
	keyval = obj.nrecvtime();
	skey->set_data(&keyval);
	skey->set_size(sizeof(time_t));
	return 0;
}

CGPSMetadataDB::CGPSMetadataDB(std::string path, std::string name, int accflag)
:m_db(NULL, 0),m_recv_index(NULL, 0), m_dbfile(path + name), accessmethod(accflag)
{
	try
	{
		m_db.set_error_stream(&std::cerr);
		m_db.set_bt_compare(gps_metakey_compare);
		m_db.open(NULL, m_dbfile.c_str(), NULL, (DBTYPE)accessmethod, DB_CREATE, 0);
		
		m_recv_index.set_error_stream(&std::cerr);
		m_recv_index.set_flags(DB_DUPSORT);
		m_recv_index.set_bt_compare(gps_recvtimeindex_compare);
		std::string strRecvindexFile = path + "recvindex_" + name;
		m_recv_index.open(NULL, strRecvindexFile.c_str(), NULL, (DBTYPE)accessmethod, DB_CREATE, 0);
		m_db.associate(NULL, &m_recv_index, gps_index_recvtime_extractor, 0);
	}
	catch(DbException &e)
	{
		std::cerr << "Error : Open DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch (std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}
}

void CGPSMetadataDB::CloseDB()
{
	try
	{
		m_recv_index.close(0);
		m_db.close(0);
		std::cout << "DB " << m_dbfile << " is closed." << std::endl;
	}
	catch(DbException &e)
	{
		std::cerr << "Error : Close DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}
}

void CGPSMetadataDB::WriteDB(time_t time, const char * pdata, int len)
{
	try
	{
		Dbt	key(&time, sizeof(time_t));
		Dbt data((void *)pdata, len);

		int ret = m_db.put(NULL, &key, &data, DB_NOOVERWRITE);
		if (ret == DB_KEYEXIST)
		{
			m_db.err(ret, "%s put failed because key %d already exists", m_dbfile.c_str(), time);
		}
	}
	catch(DbException &e)
	{
		std::cerr << "Error : Write DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}
}

int CGPSMetadataDB::ReadDB(time_t time, void * pbuf, int bufsize)
{
	try
	{
		Dbt	key(&time, sizeof(time_t));
		Dbt data;
		data.set_data(pbuf);
		data.set_ulen(bufsize);
		data.set_flags(DB_DBT_USERMEM);

		int ret = m_db.get(NULL, &key, &data, 0);
		if (ret == DB_NOTFOUND)
		{
			std::cerr << "NotFound : Read DB " << m_dbfile << " Key : " << time << std::endl;
		}
		else
		{
			return data.get_size();
		}
	}
	catch(DbException &e)
	{
		std::cerr << "Error : Read DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}

	return 0;
}

int CGPSMetadataDB::ReadNrecvtimeIndexDB(time_t time, void * pbuf, int bufsize, time_t *pkeyval)
{
	try
	{
		Dbt	skey(&time, sizeof(time_t));
		Dbt pkey, pdata;
		pdata.set_data(pbuf);
		pdata.set_ulen(bufsize);
		pdata.set_flags(DB_DBT_USERMEM);

		int ret = m_recv_index.pget(NULL, &skey, &pkey, &pdata, 0);
		if (ret == DB_NOTFOUND)
		{
			std::cerr << "NotFound : Read NrecvtimeIndex DB " << m_dbfile << " Key : " << time << std::endl;
		}
		else
		{
			*pkeyval = *((time_t *)(pkey.get_data()));
			return pdata.get_size();
		}
	}
	catch(DbException &e)
	{
		std::cerr << "Error : Read DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}

	return 0;
}

void CGPSMetadataDB::ReadDBUseCursor()
{
	Dbc * cursor = NULL;
	try
	{
		m_db.cursor(NULL, &cursor, 0);

		Dbt key, data;
		int ret;
		gps::gpsmeta obj;
		time_t tt = 0;
		int count = 0;
		while ((ret = cursor->get(&key, &data, DB_NEXT)) == 0)
		{
			std::string strData((char*)data.get_data());
			obj.ParseFromString(strData);
			tt = *((time_t *)key.get_data());
			++count;
			//std::cout << "Key : " << tt << " recvtime : " << obj.nrecvtime() << std::endl;
		}

		if (ret == DB_NOTFOUND)
		{
			std::cout << "ReadDBUseCursor Not Found." << std::endl; 
		}
		std::cout << "ReadDBUseCursor Read : " << count << std::endl;
	}
	catch(DbException &e)
	{
		std::cerr << "Error : ReadDBUseCursor DB " << m_dbfile << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}

	if (cursor != NULL)
	{
		cursor->close();
	}
}

void CGPSMetadataDB::ReadReactimeIndexDBUseCursor()
{
	Dbc *cursor;
	try
	{
		m_recv_index.cursor(NULL, &cursor, 0);
		Dbt key;
		Dbt data;

		gps::gpsmeta obj;
		time_t tt;
		int ret,count = 0;
		while ( (ret = cursor->get(&key, &data, DB_NEXT)) == 0)
		{
			std::string strData((char*)data.get_data());
			obj.ParseFromString(strData);
			tt = *((time_t *)(key.get_data()));
			++count;
			//std::cout << "Second Key : " << tt << " recvtime : " << obj.nrecvtime()  << " positiontiem : " 
			//	<< obj.npositiontime() << std::endl;
		}

		if (ret == DB_NOTFOUND)
		{
			std::cout << "ReadReactimeIndexDBUseCursor Not Found." << std::endl; 
		}
		std::cout << "ReadReactimeIndexDBUseCursor Read : " << count << std::endl;
	}
	catch(DbException &e)
	{
		std::cerr << "Error : ReadReactimeIndexDBUseCursor DB " << std::endl;
		std::cerr << e.what() << std::endl;
	}
	catch(std::exception &e)
	{
		std::cerr << "Unknown Error " << std::endl;
		std::cerr << e.what() << std::endl;
	}

	if (cursor != NULL)
	{
		cursor->close();
	}
}